home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / Hardware / VBLSnippet / VBLSnippet.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-05  |  7.5 KB  |  276 lines  |  [TEXT/MPS ]

  1. /*
  2. **    File:        VBLSnippet.c
  3. **
  4. **    Contains:    A simple example of a persistent VBL written in C that
  5. **                works with 68K or PowerPC.
  6. **
  7. **    Written by:    Jim Luther (Based on the VBL code from the Technical Note
  8. **                "TB 35 - MultiFinder Miscellanea".) 
  9. **
  10. **    Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  11. */
  12.  
  13. #include <Types.h>
  14. #include <Memory.h>
  15. #include <QuickDraw.h>
  16. #include <Fonts.h>
  17. #include <Windows.h>
  18. #include <Menus.h>
  19. #include <TextEdit.h>
  20. #include <Dialogs.h>
  21. #include <Events.h>
  22. #include <TextUtils.h>
  23. #include <Retrace.h>
  24. #include <LowMem.h>
  25. #include <Gestalt.h>
  26.  
  27. /*----------------------------------------------------------------------------*/
  28.  
  29. /*
  30. **    Define a struct to keep track of what we need in the VBL.  Put theVBLTask
  31. **    into the struct first because its address will be passed to our VBL task
  32. **    in A0.
  33. */
  34. struct VBLRec
  35. {
  36.     VBLTask        theVBLTask;        /* the VBL task itself */
  37. #if !GENERATINGCFM
  38.     long        VBLA5;            /* saved CurrentA5 where we can find it for 68K code */
  39. #endif
  40. };
  41. typedef struct VBLRec VBLRec;
  42. typedef struct VBLRec *VBLRecPtr;
  43.  
  44. /*----------------------------------------------------------------------------*/
  45.  
  46. /*
  47. **    Constants used in sample
  48. */
  49.  
  50. enum
  51. {
  52.     kInterval        =    6,        /* VBL interval */
  53.     rInfoDialog        =    140,    /* DLOG resource ID */
  54.     rStatTextItem    =    1        /* item number of counter field in dialog */
  55. };
  56.  
  57. /*----------------------------------------------------------------------------*/
  58.  
  59. /*
  60. **    Prototypes
  61. */
  62.  
  63. void        DoVBL(VBLRecPtr recPtr);
  64.  
  65. #if GENERATINGCFM
  66. void        StartVBL(VBLTaskPtr vblTaskPtr);    /* Under CFM, we're passed the VBL task pointer */
  67. #else
  68. void        StartVBL(void);                        /* Otherwise, we'll have to get it out of register A0 */ 
  69. #endif
  70.  
  71. OSErr        NewPersistentVBLProc(ProcPtr userRoutine, VBLUPP *theVBLUPP);
  72. void        DisposePersistentVBLProc(VBLUPP theVBLUPP);
  73. void        main(void);
  74.  
  75. /*----------------------------------------------------------------------------*/
  76.  
  77. /*
  78. **    A global which will be referenced from our VBL Task and the test program
  79. */
  80.  
  81. long    gCounter;        /* Counter incremented each time our VBL gets called */
  82.  
  83. /*----------------------------------------------------------------------------*/
  84.  
  85. /*
  86. **    DoVBL is called only by StartVBL()
  87. */
  88. void DoVBL(VBLRecPtr recPtr)
  89. {
  90.     gCounter++;                                    /* Show we can set a global */
  91.     recPtr->theVBLTask.vblCount = kInterval;    /* Set ourselves to run again */
  92. }
  93.  
  94. /*----------------------------------------------------------------------------*/
  95.  
  96. #if !GENERATINGCFM
  97. /*
  98. **    GetVBLRec returns the address of the VBLRec associated with our VBL task.
  99. **    This works because on entry into the VBL task, A0 points to the theVBLTask
  100. **    field in the VBLRec record, which is the first field in the record and that
  101. **    is the address we return.  Note that this method works whether the VBLRec
  102. **    is allocated globally, in the heap (as long as the record is locked in 
  103. **    memory) or if it is allocated on the stack as is the case in this example.
  104. **    In the latter case this is OK as long as the procedure which installed the
  105. **    task does not exit while the task is running.  This trick allows us to get
  106. **    to the saved A5, but it could also be used to get to anything we wanted to
  107. **    store in the record.
  108. */
  109. extern    VBLRecPtr GetVBLRec(void)
  110.     = 0x2008;    /* MOVE.L    A0,D0 */
  111. #endif
  112.  
  113. /*----------------------------------------------------------------------------*/
  114.  
  115. /*
  116. **    This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  117. **    and properly set up A5 (non-CFM only).
  118. **    Because of the vagaries of C optimization, it calls a separate routine to
  119. **    actually access global variables.  See "OV 10 - Setting and Restoring A5"
  120. **    for the reasons for this, as well as for a description of SetA5.
  121. */
  122.  
  123. #if GENERATINGCFM
  124. void StartVBL(VBLTaskPtr vblTaskPtr)
  125. {
  126.     DoVBL((VBLRecPtr)vblTaskPtr);    /* Call another routine to do actual work */
  127. }
  128. #else
  129. void StartVBL()
  130. {
  131.     long        curA5;
  132.     VBLRecPtr    recPtr;
  133.     
  134.     recPtr = GetVBLRec();            /* First get our record */
  135.     curA5 = SetA5(recPtr->VBLA5);    /* Get the saved A5 */
  136.     /* Now we can access globals */
  137.     
  138.     DoVBL(recPtr);                    /* Call another routine to do actual work */
  139.  
  140.     (void) SetA5(curA5);            /* Restore old A5 */
  141. }
  142. #endif
  143.  
  144. /*----------------------------------------------------------------------------*/
  145.  
  146. /*
  147. **    NewPersistentVBLProc allocates the VBLUPP in the System heap so the VBL
  148. **    will be persistent.
  149. */
  150. OSErr    NewPersistentVBLProc(ProcPtr userRoutine, VBLUPP *theVBLUPP)
  151. {
  152. #if GENERATINGCFM
  153.     OSErr    result;
  154.     THz        savedZone;
  155.     
  156.     savedZone = GetZone();
  157.     SetZone(SystemZone());
  158.     *theVBLUPP = NewVBLProc(userRoutine);
  159.     result = MemError();
  160.     SetZone(savedZone);
  161.     return ( result );
  162. #else
  163.     enum
  164.     {
  165.         kJMPInstr = 0x4ef9,
  166.         kJMPSize = 6
  167.     };
  168.     OSErr        result;
  169.     Ptr            sysHeapPtr;
  170.     
  171.     sysHeapPtr = NewPtrSys(kJMPSize);
  172.     result = MemError();
  173.     if ( result == noErr )
  174.     {
  175.         *(short *)sysHeapPtr = kJMPInstr;
  176.         *(ProcPtr *)(sysHeapPtr+2) = userRoutine;
  177.         FlushCodeCacheRange(sysHeapPtr, kJMPSize);
  178.         *theVBLUPP = (VBLUPP)sysHeapPtr;
  179.     }
  180.     return ( result );
  181. #endif
  182. }
  183.  
  184. /*----------------------------------------------------------------------------*/
  185.  
  186. /*
  187. **    DisposePersistentVBLProc frees up the memory used for the VBLUPP.
  188. */
  189. void    DisposePersistentVBLProc(VBLUPP theVBLUPP)
  190. {
  191. #if GENERATINGCFM
  192.     THz        savedZone;
  193.     
  194.     savedZone = GetZone();
  195.     SetZone(SystemZone());
  196.     DisposeRoutineDescriptor(theVBLUPP);
  197.     SetZone(savedZone);
  198. #else
  199.     DisposePtr((Ptr)theVBLUPP);
  200. #endif
  201. }
  202.  
  203. /*----------------------------------------------------------------------------*/
  204.  
  205. /*
  206. **    Create a dialog just to demonstrate that the global variable
  207. **    is being updated by the VBL Task.  Before installing the VBL, we store
  208. **    our A5 in the actual VBL Task record, using SetCurrentA5 described in
  209. **    OV 10 - Setting and Restoring A5.  We'll run the VBL, showing the counter
  210. **    being incremented, until the mouse button is clicked.  Then we remove
  211. **    the VBL Task, close the dialog, and remove the mouse down events to
  212. **    prevent the application from being inadvertently switched by MultiFinder.
  213. */
  214. void main (void)
  215. {
  216.     VBLRec            theVBLRec;
  217.     DialogPtr        infoDPtr;
  218.     DialogRecord    infoDStorage;
  219.     Str255            numStr = "\pTest";
  220.     OSErr            theErr;
  221.     Handle            theItemHandle;
  222.     short            theItemType;
  223.     Rect            theRect;
  224.     long            lastCount = 0;
  225.     
  226.     InitGraf(&qd.thePort);
  227.     InitFonts();
  228.     InitWindows();
  229.     InitMenus();
  230.     TEInit();
  231.     InitDialogs(NULL);
  232.     InitCursor();
  233.     MaxApplZone();
  234.     
  235. #if !GENERATINGCFM
  236.     /* Store the current value of A5 in the VBLA5 field if not CFM. */
  237.     theVBLRec.VBLA5 = SetCurrentA5 ();
  238. #endif
  239.     
  240.     gCounter = 0;    /* Initialize our global counter */
  241.     
  242.     /* Put up the dialog */
  243.     infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
  244.     DrawDialog (infoDPtr);
  245.     GetDItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, &theRect);
  246.     
  247.     /* Set the address of our routine */
  248.     theErr = NewPersistentVBLProc((ProcPtr)StartVBL, &theVBLRec.theVBLTask.vblAddr);
  249.     theVBLRec.theVBLTask.vblCount = kInterval;    /* Frequency of task, in ticks */
  250.     theVBLRec.theVBLTask.qType = vType;            /* qElement is a VBL task */
  251.     theVBLRec.theVBLTask.vblPhase = 0;
  252.     
  253.     /* Now install the VBL task */
  254.     theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
  255.     
  256.     /* Display the counter until the mouse button is pushed */
  257.     if ( theErr == noErr )
  258.     {
  259.         do
  260.         {
  261.             if (gCounter != lastCount)
  262.             {
  263.                 lastCount = gCounter;
  264.                 NumToString(gCounter, numStr);
  265.                 SetIText(theItemHandle, numStr);
  266.             }
  267.         } while ( !Button () );
  268.         theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
  269.         DisposePersistentVBLProc(theVBLRec.theVBLTask.vblAddr);    /* Dispose of the memory */
  270.     }
  271.     
  272.     /* Finish up */
  273.     CloseDialog (infoDPtr);        /* Get rid of our dialog */
  274.     FlushEvents (mDownMask, 0);    /* Flush all mouse down events */
  275. }
  276.